const crypto = require("crypto");
const fs = require("fs-extra");
const path = require("path");
const os = require("os");
const child_process = require("child_process");
const wuWxapkg = require("./src/wuWxapkg");
const wuLib = require("./src/wuLib");

function unpack(filename, premain, mainDir) {
	return new Promise((resolve, reject) => {
		let args = [filename];
		if (premain) args.push("-d");
		if (mainDir) args.push(`-s=${mainDir}`);
		let proc = child_process.fork(path.join(__dirname, "src/wuWxapkg.js"), args, {
			stdio: "inherit",
		});
		proc.on("exit", (code) => {
			if (code === 0) resolve();
			else reject(new Error("Process exited with code: " + code));
		});
	});
}

/**
 * 解密微信小程序的 wxapkg 文件
 * @param {string} wxid 微信appid
 * @param {Buffer} wxapkgBuffer wxapkg文件的Buffer
 * @returns
 */
function decrypt_wxapkg(wxid, wxapkgBuffer) {
	let dk = crypto.pbkdf2Sync(wxid, "saltiest", 1000, 32, "sha1");
	let decipher = crypto.createDecipheriv("aes-256-cbc", dk, "the iv: 16 bytes");
	let decryped = decipher.update(wxapkgBuffer.slice(6, 1024 + 6));
	let originData = Buffer.concat([decryped, decipher.final()]);
	let afData = Buffer.alloc(wxapkgBuffer.length - 1024 - 6);
	let xorKey = 0x66;
	if (wxid.length >= 2) xorKey = wxid.charCodeAt(wxid.length - 2);
	for (let i = 0; i < afData.length; i++) afData[i] = wxapkgBuffer[1024 + 6 + i] ^ xorKey;
	return Buffer.concat([originData.slice(0, 1023), afData]);
}

async function main(wxid) {
	wxid = wxid || "wx51ed0d0e39a06b7b";
	let dir = path.join(os.userInfo().homedir, "Documents/WeChat Files/Applet/", wxid);
	let files = await fs.readdir(dir);
	let version = files.pop();
	if (!version) throw new Error("No version found in: " + dir);
	dir = path.join(dir, version);
	let main = path.join(dir, "__APP__.wxapkg");
	if (!(await fs.pathExists(main))) throw new Error("No __APP__.wxapkg found in: " + dir);
	files = await fs.readdir(dir);
	files = files.filter((f) => f.endsWith(".wxapkg") && f !== "__APP__.wxapkg");
	files.unshift("__APP__.wxapkg");
	let outdir = "out/" + wxid + "/";
	await fs.remove(outdir);
	await fs.mkdirs(outdir);
	let wxapkg_list = [];
	for (let file of files) {
		let filename = path.join(dir, file);
		let buf = await fs.readFile(filename);
		let dec = decrypt_wxapkg(wxid, buf);
		let outfile = outdir + file;
		await fs.writeFile(outfile, dec);
		wxapkg_list.push(outfile);
	}
	console.log(wxapkg_list);
	await unpack(wxapkg_list[0], true);
	console.log("rename", outdir + "__APP__", outdir + "main");
	await fs.rename(outdir + "__APP__", outdir + "main");
	await unpack(wxapkg_list[0]);
	let mainout = wuLib.changeExt(wxapkg_list[0]);
	for (let i = 1; i < wxapkg_list.length; i++) {
		await unpack(wxapkg_list[i], false, path.join(process.cwd(), outdir + "main"));
		await fs.copy(wuLib.changeExt(wxapkg_list[i]), mainout);
		await fs.remove(wuLib.changeExt(wxapkg_list[i]));
		console.log("unpack", wuLib.changeExt(wxapkg_list[i]));
	}
}

main(...process.argv.slice(2))
	.catch((e) => {
		console.error(e);
		process.exit(1);
	})
	.finally(() => process.exit(0));
